<!--
  @Descripttion：导出组件
  @Author：zqy
  @Date：2020-11-04 15:25:39
  @Last Modified by：zqy
  @Last Modified time：2020-11-04 15:25:39
-->
<template>
  <el-dialog
    v-el-drag-dialog 
    :title="title"
    :visible.sync="dialogVisible"
    width="600px"
    :before-close="closeDialog"
    :close-on-click-modal="false"
    append-to-body
  >
    <div slot="title" class="custom-dialog-title">
      <span class="el-dialog__title">{{title}}</span>
      <el-button v-if="downloadUrl" type="text" style="color:#3DC094" @click="handleSeeTask">查看离线任务</el-button>
    </div>
    <el-form ref="refForm" :rules="formRules" :model="formData" size="mini" label-width="100px">
      <el-form-item label=" 类型:" prop="dataType">
        <el-radio-group v-model="formData.dataType">
            <el-radio v-if="dataConfig.length" label="data">导出数据</el-radio>
            <el-radio v-if="templateConfig.length" label="template">导出数据模板</el-radio>
          </el-radio-group>
      </el-form-item>
       <!-- v-show="nameSelectData.length>1" -->
      <el-form-item label="名称:" prop="code">
        <el-select v-model="formData.code" style="width:100%">
          <el-option v-for="(item,index) in nameSelectData" :key="index" :label="item.name" :value="item.code"/>
        </el-select>
      </el-form-item>
      <el-form-item v-show="formData.dataType==='data'" 
        label="范围:" 
        prop="type"
        :rules="[{ required: formData.dataType==='data', message: '请选择导出范围', trigger: 'change' }]"
      >
        <el-radio-group  v-model="formData.type">
            <el-radio v-if="showQueryRow" :label="1">选中的数据行</el-radio>
            <el-radio :label="2">符合当前查询结果的全部数据行</el-radio>
          </el-radio-group>
      </el-form-item>
      <el-form-item label="文件名:" prop="fileName">
        <el-input
              v-model="formData.fileName"
              placeholder="请输入导出后下载保存的文件名称"
              maxlength="50"
              style="width:100%"
            />
      </el-form-item>
    </el-form>
    <div slot="footer" class="dialog-footer">
      <el-button size="mini" @click="closeDialog">取 消</el-button>
      <el-button type="primary" size="mini" @click="handleSure">确 定</el-button>
    </div>
    
    <export-task 
      v-if="taskVisible"
      :visible.sync="taskVisible"
      :page-api="downloadUrl"
      :download-prefix="downloadPrefix"
    ></export-task>
  </el-dialog>
</template>

<script>
import {exportDataToFile} from './apis'
import ExportTask from './exportTask'
import drag from '../../directives/el-drag-dialog/drag'
export default {
  name: 'ExportFiles',
  components: {ExportTask},
  directives:{
    'el-drag-dialog':drag
  },
  props: {
    visible: {
      type: Boolean,
      default: false
    },
    title: {
      type: String,
      default: '导出数据'
    },
    downloadUrl:{
      type: String,
      default: ''
    }, // 离线下载任务接口地址
    downloadPrefix:{
      type: String,
      default: ''
    }, // 离线下载任务-单条下载url前缀
    config:{
      required: true,
      type:Array,
      default:()=>{
        return [
          {
            code:'',// 导出模板的编码(前端自定义提供)
            name:'',//导出模板的名称(前端自定义或需产品提供)
            async:false,//是否异步导出(需后端提供)
            templateCode:'',// 模板code (后端提供，若后端不需要code则可不填)
            templateApi:'',//导入模板的导出地址(后端提供)
            templateMethod:'get',//导出模板的请求方式，默认get
            templateParams:{
              // 导出模板请求参数,query为具体参数，type为传参方式（1 body方式,0 formData方式）
              query: null, //query为具体参数
              type: 0 //传参方式（0 标准filter查询，1 url携参，2 普通formData , 3 JSON）
            },      
            dataApi:'',//导出数据的地址(后端提供)
            dataMethod:'post',//导出数据的请求方式，默认post
            headers:null, // 发送请求的header参数
            filtersRaw:[],//filtersRaw参数
            paramsRaw:{},//paramRaw参数
            selectedRows:[],//选中行(用于指定行导出)
            queryField:'',//选中行导出的查询字段名称(selectedRows不为空时，此字段必填)
            exportFileName:'',// 导出文件名
            validateFunc:null,//导出操作前的校验函数，该函数需返回true/false,或者返回一个 promise
          }
        ]
      }
    },
    successFun: {
      type: [String,Function],
      default: undefined
    },
  },
  data() {
    return {
      formData: {
        dataType:'',//数据类型：data 导出数据，template 导出模板
        code:'',//导出数据名称对应编码
        type:'',//导出范围 : 1 选中的数据行 ， 2 查询范围
        fileName: ''
      },
      formRules: {
        dataType: [
          { required: true, message: '请选择导出类型', trigger: 'change' }
        ],
        code: [
          { required: true, message: '请选择名称', trigger: 'change' }
        ],
        fileName: [
          { required: true, message: '请输入导出后下载保存的文件名称', trigger: ['blur','change'] }
        ]
      },
      dataConfig:[], //数据导出配置
      templateConfig:[], // 模板导出配置
      taskVisible:false,//是否查看离线导出任务
    }
  },
  created(){
    this.dataConfig=this.config.filter(v=>!!v.dataApi)
    this.templateConfig=this.config.filter(v=> !!v.templateApi)
    const firstType=this.dataConfig.length?'data':this.templateConfig.length?'template':''
    
    this.$set(this.formData,'dataType',firstType)
    this.initExportData()
  },
  computed: {
    dialogVisible: {
      get() {
        return this.visible
      },
      set(val) {
        this.$emit('update:visible', val)
      }
    },
    // 名称选择下拉选项
    nameSelectData(){
      return  this.formData.dataType==='data' ? this.dataConfig : this.templateConfig
    },
    // 当前导出配置
    activeConfig(){
      const arr=this.config.filter(v=>{
        const flag=this.formData.dataType==='data' ?  !!v.dataApi : !!v.templateApi
        return v.code===this.formData.code  && flag
      })
      return (arr && arr[0]) || ''
    },
    showQueryRow(){
      return this.activeConfig.selectedRows && this.activeConfig.selectedRows.length && this.activeConfig.queryField
    },
  },
  watch:{
    'formData.code'(val,oldVal){
      // this.formData.dataType=''
      if(this.formData.type===1 && !this.showQueryRow){
        this.formData.type=''
      }
      this.setFileName()
    },
    'formData.dataType'(val,oldVal){
      this.formData.code=''
      this.formData.type=''
      this.setFileName()
    }
  },

  methods: {
    initExportData(){
      if(this.formData.dataType==='data'){
        this.formData.code=(this.dataConfig.length && this.dataConfig[0].code )|| ''
      }else{
        this.formData.code=(this.templateConfig.length && this.templateConfig[0].code )|| ''
      }
      this.setFileName()
    },
    setFileName(){
      let fileName=''
      if(this.formData.code){
        const arr=this.config.filter(v=>v.code===this.formData.code)
        if(arr && arr.length){
          const newDate=new Date()
          let name = arr[0].exportFileName ||  ''
          if(name===''){
            name = arr[0].name
            if(this.formData.dataType==='template'){
              name=`${name}${name.indexOf('模板')===-1?'模板':''}`
            }else{
              name=`${name}${name.indexOf('数据')===-1?'数据':''}`
            }
          }
          const time=`${newDate.getFullYear()}${newDate.getMonth()+1}${newDate.getDate()}${newDate.getHours()}${newDate.getMinutes()}${newDate.getSeconds()}`
          fileName=`${name}${time}`
        }
      }
      this.$set(this.formData,'fileName',fileName)
    },
    closeDialog() {
      this.dialogVisible = false
      this.$refs.refForm && this.$refs.refForm.clearValidate()
      this.initExportData()
      this.clearForm()
    },
    clearForm(){
      this.$set(this.formData,'fileName','')
      this.$set(this.formData,'dataType','')
      this.$set(this.formData,'type','')
    },
    // 确定下载
    handleSure(){
      if (this.submiting) return
      this.$refs['refForm'].validate((valid) => {
        if (valid) {
          const fileName=this.formData.fileName
          if(this.formData.type===1 && this.activeConfig.selectedRows.length===0){
            this.$message.warning('请先选择需要导出的数据行')
            return
          }

          // 检查是否存在校验函数，若存在则执行
          const validateFunc = this.activeConfig.validateFunc
          if(validateFunc instanceof Function){
            const result = validateFunc()
            if (result instanceof Promise) {
              return result.then(res => {
                // 执行导出
                if(this.formData.dataType==='template'){
                  this.exportTemplateData(fileName)
                  return
                }
                this.exportTableData(fileName)
              })
            } else {
              if(!result){
                return
              }
            }
          }

          if(this.formData.dataType==='template'){
            this.exportTemplateData(fileName)
            return
          }
          this.exportTableData(fileName)
        }
      })
    },
    // 导出模板
    exportTemplateData(fileName){
      this.submiting = true
      const loading = this.$loading({
        lock: true,
        text: '正在导出模板,请耐心等待',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.2)',
        customClass:'custom-loading'
      });
      let templateMethod = this.activeConfig.templateMethod || 'get'
      const {templateApi,headers = null, templateParams={} ,templateCode=''}=this.activeConfig  //下载导入模板共用接口：应用前缀+'/api/v1/excel/template'
      
      let apiUrl = templateApi

      const filtersRaw = []
      const obj = { ...templateParams.query }
      if(![null,undefined,''].includes(templateCode)){
        obj.templateCode = templateCode
      }
      if (obj && Object.keys(obj).length>0) {
        for (const i in obj) {
          if (Array.isArray(obj[i]) && obj[i].length === 2) {
            filtersRaw.push({
              id: Math.floor(Math.random() * 0xffffffffff).toString(16),
              value: obj[i][0],
              property: i,
              operator: '>='
            })
            filtersRaw.push({
              id: Math.floor(Math.random() * 0xffffffffff).toString(16),
              value: obj[i][1],
              property: i,
              operator: '<='
            })
          } else if (obj.hasOwnProperty(i) && Object.prototype.toString.call(obj[i]) === '[object Object]') {
            const v = obj[i]
            if (v.value instanceof Array) {
              if (v.value.length <= 0) {
                continue
              } else {
                v.value = `[${v.value.join(',')}]`
              }
            }
            filtersRaw.push({
              id: Math.floor(Math.random() * 0xffffffffff).toString(16),
              value: v.value,
              property: i,
              operator: v.operator
            })
          } else if (obj[i] !== '[]' && obj[i] !== '' && obj[i] !== undefined && obj[i] !== null) {
            filtersRaw.push({
              id: Math.floor(Math.random() * 0xffffffffff).toString(16),
              value: obj[i],
              property: i,
              operator: 'like'
            })
          }
        }
      }
      const paramType = templateParams.type
      let reqData = ''
      if(paramType === 0){
        // 标准filter查询
        reqData = new FormData() 
        reqData.append('page', 1)
        reqData.append('rows', 50)
        reqData.append('filtersRaw', JSON.stringify(filtersRaw))
        if(templateCode){
          reqData.append('paramsRaw',JSON.stringify({templateCode}))
        }
      }else if(paramType === 3){
        // JSON数据
        reqData={...obj}
      }else if(paramType===2){
        // 普通formData
        reqData= new FormData()
        for (const key in obj) {
          reqData.append(key, obj[key])
        }
      }else if(paramType===1){
        const strList = []
        for (const i in obj) {
          if (obj[i]) {
            strList.push(i + '=' + obj[i])
          }
        }
        if(strList.length>0){
          apiUrl= `${apiUrl}?${strList.join('&')}`
        }        
      }else{
        reqData={ templateCode }
      }

      exportDataToFile(apiUrl,templateMethod,reqData,headers,false).then(response => {
        const res = response.data
        if(!res || res.type==="application/json" || res.type==="text/html"){
          let reader = new FileReader(); // 创建读取文件对象
          reader.readAsText(res, 'utf-8'); // 设置读取的数据以及返回的数据类型为utf-8
          reader.onloadend = () => {
            let result = JSON.parse(reader.result); // 返回的数据
            this.$message.warning(result.msg || '模板导出失败')
            reader = null
          }         
          this.submiting = false
          loading.close()
          return
        }
        this.createDownload(response, fileName)
        this.submiting = false
        loading.close()
        this.dialogVisible=false
        this.clearForm()

        if(this.successFun){
          if(this.successFun instanceof Function){
            this.successFun(this.activeConfig,res)
          }else{
            this.$message.success(this.successFun)
          }
        }else{
          this.$message.success('模板导出成功,正在下载')
        }
        this.$emit('success')
      }).catch((err) => {
        console.log(err)
        this.$message.warning('模板导出失败')
        this.submiting = false
        loading.close()
      })
    },
    // 导出数据
    exportTableData(fileName){
      const activeConfig = JSON.parse(JSON.stringify(this.activeConfig))
      const {
        dataApi,
        headers = null,
        filtersRaw = [],
        paramsRaw = {},
        selectedRows = [],
        sort ='',
        order ='',
        queryField = '',
        dataMethod='post',
        async = false
      } = activeConfig

      if(!dataApi){
        this.$message.warning('导出数据参数配置异常')
        return 
      }
      if(this.formData.type===1 && selectedRows.length){
        if(!selectedRows[0][queryField]){
          this.$message.warning('选中行导出参数匹配失败')
          return
        }
        const filterValues=selectedRows.map(v=>{
          return v[queryField]
        })
        filtersRaw.push({
          id:queryField,
          property:queryField,
          operator:'in',
          value:`[${filterValues.join(',')}]`
        })
      }
      
      if(this.activeConfig.templateCode){
        paramsRaw.templateCode = this.activeConfig.templateCode
      }
      if(this.activeConfig.async){
        paramsRaw.taskName = fileName
      }

      const requestParam = new FormData()
      requestParam.append('pageFlag', false)
      requestParam.append('filtersRaw', JSON.stringify(filtersRaw || []))
      requestParam.append('paramsRaw', JSON.stringify(paramsRaw || {}))
      if(sort){
        requestParam.append('sort', sort)
        requestParam.append('order', order || 'desc')
      }

      this.submiting = true
      const loading = this.$loading({
        lock: true,
        text: '正在导出数据,请耐心等待',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.2)',
        customClass:'custom-loading'
      });
      exportDataToFile(dataApi,dataMethod,requestParam,headers,async).then(response => {
        /* if(!response || !response.data){
          this.$message.warning('登录已失效，请重新登录。(您的账号可能已在别处登录，请妥善保存密码，勿与他人共用账号。)')
          this.submiting = false
          loading.close()
          return
        } */
        const res = response.data
        if(!async){
          // 同步下载
          if(!res || res.type==="application/json" || res.type==="text/html"){
            let reader = new FileReader(); // 创建读取文件对象
            reader.readAsText(res, 'utf-8'); // 设置读取的数据以及返回的数据类型为utf-8
            reader.onloadend = () => {
              let result = JSON.parse(reader.result); // 返回的数据
              this.$message.warning(result.msg || '数据导出失败')
              reader = null
            }
            /* const loadendFunc = ()=>{
              let result = JSON.parse(reader.result); // 返回的数据
              this.$message.warning(result.msg || '数据导出失败')
              reader.removeEventListener('loadend',loadendFunc)
              reader = null
            }
            reader.addEventListener("loadend",loadendFunc) */
            /* reader.addEventListener("loadend", ()=>{
              let result = JSON.parse(reader.result); // 返回的数据
              this.$message.warning(result.msg || '数据导出失败')
              reader.removeEventListener('loadend')
              reader = null
            }) */

            this.submiting = false
            loading.close()
            return
          }
          this.createDownload(response, fileName)
        }else{
          // 异步下载
          if(res && res.success===0){
            this.$message.error(res.msg || '数据导出失败')
            this.submiting = false
            loading.close()
            return
          }
        }

        if(this.successFun){
          if(this.successFun instanceof Function){
            this.successFun(this.activeConfig,res)
          }else{
            this.$message.success(this.successFun)
          }
        }else{
          this.$message.success(!async?'数据导出成功,正在下载':'数据导出执行成功,请查看离线任务进行下载')
        }

        this.submiting = false
        loading.close()
        this.dialogVisible=false
        this.clearForm()
        this.$emit('success',async?res:'')
      }).catch((err) => {
        console.log(err)
        this.$message.warning('导出失败')
        this.submiting = false
        loading.close()
      })
    },
    /**
     * 创建下载
     * @param {blob} data 文件流
     * @param {string} fileName  文件名称
     * */ 
    createDownload(res, fileName) {
      if(!res || !res.data) return
      if (fileName) {
        fileName = `${decodeURIComponent(fileName)}.xlsx`
      } else {
        fileName = res.headers['content-disposition']
        fileName = fileName.split(';')[1].split('filename=')[1]
        const fileNameUnicode = res.headers['content-disposition'].split('filename*=')[1]
        if (fileNameUnicode) { // 当存在 filename* 时，取filename* 并进行解码（为了解决中文乱码问题）
          fileName = decodeURIComponent(fileNameUnicode.split("''")[1])
        } else {
          fileName = decodeURIComponent(fileName)
        }
      }
      const blob = new Blob([res.data], {type: "application/vnd.ms-excel;charset=utf-8"})
      if (window.navigator.msSaveOrOpenBlob) {
        navigator.msSaveBlob(blob, fileName)
      } else {
        const link = document.createElement('a')
        const objectURL = URL.createObjectURL(blob)
        link.download = decodeURI(fileName)
        link.href = objectURL
        document.body.appendChild(link)
        link.click()
        window.URL.revokeObjectURL(objectURL);
        document.body.removeChild(link)
      }
    },
    // 查看离线任务
    handleSeeTask(){
      /* window.parent.postMessage({
        flag: 'portalPage',
        extraData: {
          name:'ExportTask',
          query:{}
        }
      }, '*')
      */
      this.taskVisible = true
    }
  }
}
</script>
<style lang='scss' scoped>
/* @import url(); 引入css类 */
</style>
